package com.eva.news.client.core.config.cms;

import com.alibaba.fastjson.JSONObject;
import com.eva.news.cms.biz.template.dto.BuildTemplateDataDTO;
import com.eva.news.dao.cms.model.CmsArticle;
import com.eva.news.framework.cache.CacheProxy;
import com.eva.news.framework.common.servlet.ServletDuplicateInputStream;
import com.eva.news.framework.common.servlet.ServletDuplicateRequestWrapper;
import com.eva.news.framework.common.utils.Utils;
import com.eva.news.framework.config.AppConfig;
import com.eva.news.service.cms.CmsArticleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 文章详情访问次数统计拦截器
 * 注意：前端为Nuxt3框架，会在服务端和浏览器端分别请求文章详情模板数据，所以每次文章详情会触发2次拦截.
 */
@Slf4j
@Component
public class CmsArticleViewCountInterceptor implements HandlerInterceptor {

    @Resource
    private AppConfig appConfig;

    @Resource
    private CmsArticleService cmsArticleService;

    @Resource
    private CacheProxy<String, Boolean> cacheProxy;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        try {
            if (!(request instanceof ServletDuplicateRequestWrapper)) {
                return true;
            }
            // 获取请求参数
            ServletDuplicateRequestWrapper requestWrapper = (ServletDuplicateRequestWrapper) request;
            ServletDuplicateInputStream servletInputStream = (ServletDuplicateInputStream) requestWrapper.getInputStream();
            BuildTemplateDataDTO dto = JSONObject.parseObject(servletInputStream.getBody(), BuildTemplateDataDTO.class);
            // 访问的是详情页面，viewTemplatePath 为文章详情页模板地址
            String viewTemplatePath = "/article/[articleUid].vue";
            if (viewTemplatePath.equals(dto.getPath())) {
                if (dto.getParameters() == null) {
                    log.warn("已忽略文章详情访问次数统计，接口未携带模板参数");
                    return true;
                }
                // 获取文章UID
                String articleUid = dto.getParameters().getString("articleUid", null);
                if (articleUid == null) {
                    log.warn("已忽略文章详情访问次数统计，未能找到文章唯一标识");
                    return true;
                }
                // 查询文章详情
                CmsArticle article = cmsArticleService.findByUid(articleUid);
                if (article == null) {
                    log.warn("已忽略文章详情访问次数统计，未能找到对应文章，文章唯一标识: {}", articleUid);
                    return true;
                }

                // 缓存文章访问，实现同IP在一定时间内的多次访问视为一次访问
                String ip = Utils.User_Client.getIP(request);
                // - 将ip中的“:”改为"."（本地访问时，ip中带冒号）
                ip = StringUtils.replace(ip, ":", ".");
                // - 组件缓存键
                String cacheKey = "article:view:" + ip + ":interval:" + articleUid;
                // - 如果为多次访问，忽略统计
                if (cacheProxy.get(cacheKey) != null) {
                    return true;
                }
                // - 记录请求缓存，并在 appConfig.getArticleViewCountInterval() 后过期，实现下次未超时时访问时，不统计访问次数
                cacheProxy.put(cacheKey, Boolean.TRUE, appConfig.getArticleViewCountInterval());

                // 修改访问次数：文章访问次数加1
                CmsArticle newCmsArticle = CmsArticle.builder()
                        .id(article.getId())
                        .viewCount(article.getViewCount() + 1)
                        .build();
                cmsArticleService.updateById(newCmsArticle);
            }
            return true;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
